﻿// -------------------------------------------------------------------------
//    @FileName         :    NFQuestUtil.cpp
//    @Author           :    gaoyi
//    @Date             :    2025/1/15
//    @Email            :    445267987@qq.com
//    @Module           :    NFQuestUtil
//
// -------------------------------------------------------------------------

#include "NFQuestUtil.h"

#include <NFPlayer.h>
#include <NFQuestPart.h>
#include <NFServiceHero.h>
#include <NFServiceLevel.h>
#include <NFServicePackage.h>
#include <NFServiceSkill.h>
#include <NFServiceSns.h>
#include <DescStore/LoopQuestDesc.h>

#include "NFPlayerUtil.h"
#include "proto_cs_cmd.nanopb.h"
#include "proto_svr_common.nanopb.h"
#include "proto_svr_quest.pb.h"
#include "DescStore/QuestDesc.h"


int NFQuestUtil::CheckAcceptNormal(NFQuestPart* pQuestPart, const E_Quest* pDesc, const bool bNotCheckFinish)
{
    CHECK_NULL(0, pQuestPart);
    const auto pPlayer = pQuestPart->GetPlayer();
    CHECK_NULL(0, pPlayer);
    CHECK_NULL(0, pDesc);
    if (!bNotCheckFinish && pQuestPart->IsFinished(pDesc->id))
    {
        LOG_ERR(pPlayer->GetRoleID(), ERR_CODE_QUEST_ALEADY_FINISH, "quest:{} already finish", pDesc->id);
        return ERR_CODE_QUEST_ALEADY_FINISH;
    }

    return CheckAcceptCommon(pQuestPart, pDesc);
}

int NFQuestUtil::CheckAcceptCommon(NFQuestPart* pQuestPart, const E_Quest* pDesc, const bool bNotCheckTeam)
{
    CHECK_NULL(0, pQuestPart);
    const auto pPlayer = pQuestPart->GetPlayer();
    CHECK_NULL(0, pPlayer);
    CHECK_NULL(0, pDesc);

    const int iQuestId = pDesc->id;
    const int iLevel = pPlayer->GetLevel();

    if (pDesc->is_hide)
    {
        CHECK_WARN(pPlayer->GetRoleID(), ERR_CODE_QUEST_IS_HIDE, "can not accept hided quest:{}", iQuestId);
    }

    if (pDesc->start_date_t > 0 && pDesc->start_date_t > (uint64_t)NF_ADJUST_TIMENOW())
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_DATE_NOT_MATH, "Quest Not Start:{} now:{} start ts:{}", pDesc->start_date.ToString(), NF_ADJUST_TIMENOW(), pDesc->start_date_t);
    }

    if (pDesc->end_date_t > 0 && pDesc->end_date_t < (uint64_t)NF_ADJUST_TIMENOW())
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_DATE_NOT_MATH, "Quest Not End:{} now:{} end ts:{}", pDesc->end_date.ToString(), NF_ADJUST_TIMENOW(), pDesc->end_date_t);
    }

    if (pDesc->accept_level_min > iLevel)
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_LEVEL_NOT_ENOUGH, "level not enough, level:{} iQuestId:{} min level:{}", iLevel, iQuestId, pDesc->accept_level_min);
    }

    if (pDesc->round_group_id == 0 && pDesc->accept_level_max > 0 && pDesc->accept_level_max < iLevel)
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_LEVEL_TO_MUCH, "level to much, level:{} iQuestId:{} max level:{}", iLevel, iQuestId, pDesc->accept_level_max);
    }

    if (pDesc->accept_at_guild && NFServiceSns::GetGuildID(pPlayer) == 0)
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_PLAYER_NOT_IN_GUILD, "player not in guild");
    }

    if (pDesc->accept_check_item > 0 && NFServicePackage::GetItemTotalNum(pPlayer, pDesc->accept_check_item) == 0)
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_ITEM_NOT_ENOUGH, "accept check item:{} not enough", pDesc->accept_check_item);
    }

    if (!bNotCheckTeam && pDesc->accept_with_team_member_num > 0)
    {
        int iRetCode = 0;
        iRetCode = CheckTeam(pPlayer, iQuestId, pDesc->accept_with_team_member_num, pDesc->accept_level_min, pDesc->accept_level_max, pDesc->accept_at_guild);
        if (iRetCode)
            return iRetCode;
    }

    if (pDesc->accept_by_job > 0 && pDesc->accept_by_job != NFServiceHero::GetHeroJob(pPlayer))
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_JOB_NOT_MATCH, "job not match, accept_by_job:{} job:{}", pDesc->accept_by_job, NFServiceHero::GetHeroJob(pPlayer));
    }

    if (pDesc->accept_need_transfer && NFServiceSkill::IsTransfer(pPlayer))
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_ROLE_NOT_TRANSFER, "player not transfer");
    }

    return 0;
}

int NFQuestUtil::CheckTeam(NFPlayer* pPlayer, int iQuestId, int iMemberNum, int iLevelMin, int iLevelMax, bool inGuild)
{
    return 0;
}

int NFQuestUtil::InitQuestNode(NFPlayer* pPlayer, QuestNode* pNode, const E_Quest* pDesc)
{
    CHECK_NULL(0, pPlayer);
    CHECK_NULL(pPlayer->GetRoleID(), pNode);
    CHECK_NULL(pPlayer->GetRoleID(), pDesc);
    pNode->accept_time = NF_ADJUST_TIMENOW();
    pNode->event_node_list.clear();
    if (pDesc->complete_level > 0)
    {
        QuestEventNode eventNode;
        eventNode.ev = E_QUEST_EVENT_LEVEL_UP;
        eventNode.condition = 0;
        eventNode.condition_value = pDesc->complete_level;
        eventNode.value = pPlayer->GetLevel();
        pNode->event_node_list.push_back(eventNode);
    }
    if (pDesc->complete_random_item_weight_sum > 0)
    {
        int currRateSum = 0;
        for (int i = 0; i < (int)pDesc->complete_random_item.size(); ++i)
        {
            if (pDesc->complete_random_item[i].id == 0 || pDesc->complete_random_item[i].num == 0)
                continue;

            if (currRateSum + pDesc->complete_random_item[i].weight >= pDesc->complete_random_item_weight_sum)
            {
                QuestEventNode eventNode;
                eventNode.ev = E_QUEST_EVENT_CONSUME_ITEM;
                eventNode.condition = pDesc->complete_random_item[i].id;
                eventNode.condition_value = pDesc->complete_random_item[i].num;
                eventNode.value = NFServicePackage::GetItemTotalNum(pPlayer, pDesc->complete_random_item[i].id);
                pNode->event_node_list.push_back(eventNode);
                break;
            }
            currRateSum += pDesc->complete_random_item[i].weight;
        }
    }

    if (pDesc->complete_level_id > 0)
    {
        QuestEventNode eventNode;
        if (pDesc->complete_level_time)
        {
            eventNode.ev = E_QUEST_EVENT_COMPLETE_LEVEL_TIME;
        }
        else if (pDesc->complete_level_star)
        {
            eventNode.ev = E_QUEST_EVENT_COMPLETE_LEVEL_STAR;
        }
        else
        {
            eventNode.ev = E_QUEST_EVENT_COMPLETE_LEVEL;
        }
        eventNode.condition = pDesc->complete_level_id;
        eventNode.condition_value = 1;
        eventNode.value = NFServiceLevel::CharpterIsBitSetted(pPlayer, pDesc->complete_level_id);
        pNode->event_node_list.push_back(eventNode);
    }

    if (pDesc->complete_join_guild)
    {
        QuestEventNode eventNode;
        eventNode.ev = E_QUEST_EVENT_JOIN_GUILD;
        eventNode.condition = 0;
        eventNode.condition_value = 1;
        if (NFServiceSns::IsInGuild(pPlayer))
        {
            eventNode.value = 1;
        }
        else
        {
            eventNode.value = 0;
        }
        pNode->event_node_list.push_back(eventNode);
    }

    if (pDesc->complete_equip_strength_times)
    {
        QuestEventNode eventNode;
        eventNode.ev = E_QUEST_EVENT_STRENGTHEN;
        eventNode.condition = 0;
        eventNode.condition_value = pDesc->complete_equip_strength_times;
        eventNode.value = 0;
        pNode->event_node_list.push_back(eventNode);
    }

    if (pDesc->complete_level_mode_times)
    {
        QuestEventNode eventNode;
        eventNode.ev = E_QUEST_EVENT_COMPLETE_LEVEL_WITH_MODE;
        eventNode.condition = pDesc->complete_level_type;
        eventNode.condition_value = pDesc->complete_level_mode_times;
        eventNode.value = 0;
        pNode->event_node_list.push_back(eventNode);
    }
    if (pDesc->complete_skill_awake)
    {
        QuestEventNode eventNode;
        eventNode.ev = E_QUEST_EVENT_COMPLETE_SKILL_AWAKE;
        eventNode.condition = 0; // awake index
        eventNode.condition_value = 1;
        eventNode.value = NFServiceSkill::IsAwake(pPlayer);
        pNode->event_node_list.push_back(eventNode);
    }
    if (pDesc->complete_skill_transfer)
    {
        QuestEventNode eventNode;
        eventNode.ev = E_QUEST_EVENT_COMPLETE_SKILL_TRANSFER;
        eventNode.condition = 0; // awake index
        eventNode.condition_value = 1;
        eventNode.value = NFServiceSkill::IsTransfer(pPlayer);
        pNode->event_node_list.push_back(eventNode);
    }
    if (pDesc->complete_instance)
    {
        QuestEventNode eventNode;
        eventNode.ev = E_QUEST_EVENT_COMPLETE_INSTANCE;
        eventNode.condition = pDesc->complete_instance; // instance id
        eventNode.condition_value = 1;
        eventNode.value = 0;
        pNode->event_node_list.push_back(eventNode);
    }
    if (pDesc->complete_about_loop_id && pDesc->complete_about_loop_quest_times)
    {
        QuestEventNode eventNode;
        eventNode.ev = E_QUEST_EVENT_COMPLETE_LOOP_QUEST_WITH_TIMES;
        eventNode.condition = pDesc->complete_about_loop_id;
        eventNode.condition_value = pDesc->complete_about_loop_quest_times;
        pNode->event_node_list.push_back(eventNode);
    }
    if (pDesc->complete_levels_id_list.size() > 0)
    {
        QuestEventNode eventNode;
        eventNode.condition = 0;
        if (pDesc->complete_level_star)
        {
            eventNode.ev = E_QUEST_EVENT_COMPLETE_LEVEL_STAR;
            eventNode.condition_value = 1;
            pNode->event_node_list.push_back(eventNode);
        }
        else if (pDesc->complete_level_time)
        {
            eventNode.ev = E_QUEST_EVENT_COMPLETE_LEVEL_TIME;
            eventNode.condition_value = 1;
            pNode->event_node_list.push_back(eventNode);
        }
        else if (pDesc->complete_enter_any_level)
        {
            if (pDesc->complete_levels_win)
            {
                eventNode.ev = E_QUEST_EVENT_COMPLETE_LEVEL;
            }
            else
            {
                eventNode.ev = E_QUEST_EVENT_ENTER_LEVEL;
            }
            eventNode.condition_value = 1;
            pNode->event_node_list.push_back(eventNode);
        }
    }
    for (int i = 0; i < (int)pDesc->complete_item.size() && pNode->event_node_list.size() < pNode->event_node_list.max_size(); ++i)
    {
        if (pDesc->complete_item[i].id <= 0 || pDesc->complete_item[i].num <= 0)
            continue;

        QuestEventNode eventNode;
        eventNode.ev = E_QUEST_EVENT_CONSUME_ITEM;
        eventNode.condition = pDesc->complete_item[i].id;
        eventNode.condition_value = pDesc->complete_item[i].num;
        eventNode.value = NFServicePackage::GetItemTotalNum(pPlayer, pDesc->complete_item[i].id);
        pNode->event_node_list.push_back(eventNode);
    }
    for (int i = 0; i < (int)pDesc->complete_kill_monster_list.size() && pNode->event_node_list.size() < pNode->event_node_list.max_size(); ++i)
    {
        if (pDesc->complete_kill_monster_list[i].id <= 0 || pDesc->complete_kill_monster_list[i].num <= 0)
            continue;
        QuestEventNode eventNode;
        eventNode.ev = E_QUEST_EVENT_KILL_MONSTER;
        eventNode.condition = pDesc->complete_kill_monster_list[i].id;
        eventNode.condition_value = pDesc->complete_kill_monster_list[i].num;
        eventNode.value = 0;
        pNode->event_node_list.push_back(eventNode);
    }

    for (int i = 0; i < (int)pDesc->complete_find_npc.size() && pNode->event_node_list.size() < pNode->event_node_list.max_size(); ++i)
    {
        if (pDesc->complete_find_npc[i])
        {
            QuestEventNode eventNode;
            eventNode.ev = E_QUEST_EVENT_DAILOG_WITH_NPC;
            eventNode.condition = pDesc->complete_find_npc[i];
            eventNode.condition_value = 1;
            pNode->event_node_list.push_back(eventNode);
        }
    }
    return 0;
}

int NFQuestUtil::CheckAcceptRound(NFQuestPart* pQuestPart, const LoopQuest* pLoop, const E_Quest* pDesc, const bool bNotCheckTeam)
{
    const int iQuestId = pDesc->id;
    if (pLoop->quest_id != iQuestId)
    {
        LOGSVR_ERROR("quest id error, need: " << pLoop->quest_id << " Get:" << iQuestId);
        return proto_ff::ERR_CODE_INVALID_REQ_PARAM;
    }
    const int iRet = CheckAcceptCommon(pQuestPart, pDesc, bNotCheckTeam);
    CHECK_RET(iRet, "Check Quest Accept Condition Failed: {}", iQuestId);

    const auto pLoopDesc = LoopQuestDesc::Instance()->GetDesc(pDesc->round_group_id);
    CHECK_NULL(0, pLoopDesc);

    return CheckAcceptCommon(pQuestPart, pLoopDesc, bNotCheckTeam);
}

int NFQuestUtil::CheckAcceptCommon(NFQuestPart* pQuestPart, const E_LoopQuest* pDesc, bool bNotCheckTeam)
{
    CHECK_NULL(0, pQuestPart);
    auto pPlayer = pQuestPart->GetPlayer();
    CHECK_NULL(0, pPlayer);
    CHECK_NULL(0, pDesc);

    int iQuestId = pDesc->id;
    int iLevel = pPlayer->GetLevel();

    if (pDesc->is_hide)
    {
        CHECK_WARN(pPlayer->GetRoleID(), ERR_CODE_QUEST_IS_HIDE, "can not accept hided quest:{}", iQuestId);
    }

    if (pDesc->accept_start_date_t > 0 && pDesc->accept_start_date_t > (uint64_t)NF_ADJUST_TIMENOW())
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_DATE_NOT_MATH, "Quest Not Start:{} now:{} start ts:{}", pDesc->accept_start_date.ToString(), NF_ADJUST_TIMENOW(), pDesc->accept_start_date_t);
    }

    if (pDesc->accept_end_date_t > 0 && pDesc->accept_end_date_t < (uint64_t)NF_ADJUST_TIMENOW())
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_DATE_NOT_MATH, "Quest Not End:{} now:{} end ts:{}", pDesc->accept_end_date.ToString(), NF_ADJUST_TIMENOW(), pDesc->accept_end_date_t);
    }

    if (pDesc->accept_level_min > iLevel)
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_LEVEL_NOT_ENOUGH, "level not enough, level:{} iQuestId:{} min level:{}", iLevel, iQuestId, pDesc->accept_level_min);
    }

    if (pDesc->accept_level_max > 0 && pDesc->accept_level_max < iLevel)
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_LEVEL_TO_MUCH, "level to much, level:{} iQuestId:{} max level:{}", iLevel, iQuestId, pDesc->accept_level_max);
    }

    if (pDesc->accept_at_guild && NFServiceSns::GetGuildID(pPlayer) == 0)
    {
        CHECK_ERR(pPlayer->GetRoleID(), ERR_CODE_PLAYER_NOT_IN_GUILD, "player not in guild");
    }

    if (!bNotCheckTeam && pDesc->accept_with_team_member_num > 0)
    {
        int iRetCode = 0;
        iRetCode = CheckTeam(pPlayer, iQuestId, pDesc->accept_with_team_member_num, pDesc->accept_level_min, pDesc->accept_level_max, pDesc->accept_at_guild);
        if (iRetCode)
            return iRetCode;
    }

    if (pDesc->check_week_day)
    {
        bool dayChecked = false;
        for (int i = 0; i < (int)pDesc->accept_by_weekday.size(); i++)
        {
            if (pDesc->accept_by_weekday[i] > 0 && NFTimeUtility::GetWeekDay127(NF_ADJUST_TIMENOW()) == pDesc->accept_by_weekday[i])
            {
                dayChecked = true;
                break;
            }
        }
        if (!dayChecked)
        {
            LOGSVR_WARN("weekday error, now:" << NFTimeUtility::GetWeekDay127(NF_ADJUST_TIMENOW()));
            return proto_ff::ERR_CODE_DATE_NOT_MATH;
        }
    }

    if (pDesc->loop_start_time)
    {
        uint32_t now = NF_ADJUST_TIMENOW();
        if (now < NFTimeUtility::GetTodayStartTime(now) + pDesc->loop_start_time)
        {
            LOGSVR_WARN("LoopQuest Not Start: " << pDesc->id << " " << pDesc->loop_start_time_str.ToString());
            return proto_ff::ERR_CODE_DATE_NOT_MATH;
        }
    }
    if (pDesc->loop_end_time)
    {
        uint32_t now = NF_ADJUST_TIMENOW();
        if (now > NFTimeUtility::GetTodayStartTime(now) + pDesc->loop_end_time)
        {
            LOGSVR_WARN("LoopQuest Already End: " << pDesc->id << " " << pDesc->loop_end_time_str.ToString());
            return proto_ff::ERR_CODE_DATE_NOT_MATH;
        }
    }

    return 0;
}

int NFQuestUtil::CheckAcceptAfterFinish(int iPrev, int iNext, NFQuestPart* pQuestPart)
{
    auto pDesc = QuestDesc::Instance()->GetDesc(iNext);
    CHECK_EXPR(pDesc, proto_ff::ERR_CODE_INVALID_REQ_PARAM, "Quest Not Exist:{}", iNext);

    // 仅用于支线任务
    CHECK_EXPR(pDesc->round_group_id == 0, proto_ff::ERR_CODE_INVALID_REQ_PARAM, "Loop Quest Cant Accpet By Complete:{} ", iNext);

    int iRet = CheckAcceptCommon(pQuestPart, pDesc);
    CHECK_RET(iRet, "check failed");

    return iRet;
}

int NFQuestUtil::CalcQuestExpMoney(NFPlayer* pPlayer, const E_Quest* pDesc, double& exp, double& money, double& pet_exp, double& nian, double& contri, double& coin, double& bonus)
{
    CHECK_NULL(0, pPlayer);
    int iPlayerLevel = pPlayer->GetLevel();
    money = (double)pDesc->award_money + CalcQuestAward(pDesc->award_money_function, pDesc->award_money_param_1, pDesc->award_money_param_2, pDesc->quest_used_time, iPlayerLevel);
    exp = (double)pDesc->award_exp + CalcQuestAward(pDesc->award_exp_function, pDesc->award_exp_param_1, pDesc->award_exp_param_2, pDesc->quest_used_time, iPlayerLevel);
    exp += NFPlayerUtil::CalcExpBonus(pPlayer, exp);

    pet_exp = 0;
    nian = (double)pDesc->award_nian_tax + CalcNianAward(pDesc->award_nian_tax_function, pDesc->award_nian_tax_param_1, pDesc->award_nian_tax_param_2, pDesc->quest_used_time, iPlayerLevel);
    contri = 0;
    coin = 0;
    bonus = 0;
    if (NFServiceSns::IsInGuild(pPlayer) == false)
    {
        return 0;
    }

    contri = (double)pDesc->award_guild_tax + CalcQuestAward(pDesc->award_guild_tax_function, pDesc->award_guild_tax_param_1, pDesc->award_guild_tax_param_2, pDesc->quest_used_time, iPlayerLevel);
    coin = (double)pDesc->award_guild_coin + CalcQuestAward(pDesc->award_guild_coin_function, pDesc->award_guild_coin_param_1, pDesc->award_guild_coin_param_2, pDesc->quest_used_time, iPlayerLevel);
    bonus = (double)pDesc->award_guild_coin_pool + CalcQuestAward(pDesc->award_guild_coin_pool_function, pDesc->award_guild_coin_pool_param_1, pDesc->award_guild_coin_pool_param_2, pDesc->quest_used_time, iPlayerLevel);
    return 0;
}

double NFQuestUtil::CalcQuestAward(int funId, int param1, int param2, int time, int playerLevel)
{
    switch (funId)
    {
        case 1:
            if (playerLevel > 0)
            {
                return (playerLevel - 1) * param1 * param2 * time / 60;
            }
            break;
        case 2:
            if (playerLevel > 0)
            {
                return ((playerLevel - 1) * param1 + param2) * time / 60;
            }
        default:
            break;
    }
    return 0;
}

double NFQuestUtil::CalcQuestAward(int funId, float param1, float param2, int time, int playerLevel)
{
    switch (funId)
    {
        case 1:
            if (playerLevel > 0)
            {
                return (playerLevel - 1) * param1 * param2 * time / 60;
            }
            break;
        case 2:
            if (playerLevel > 0)
            {
                return ((playerLevel - 1) * param1 + param2) * time / 60;
            }
        default:
            break;
    }
    return 0;
}

double NFQuestUtil::CalcNianAward(int funId, float param1, float param2, int time, int playerLevel)
{
    switch (funId)
    {
        case 1:
            if (playerLevel > 0)
            {
                return ((double)(playerLevel - 1)) * param1 * param2;
            }
            break;
        case 2:
            if (playerLevel > 0)
            {
                return ((double)((playerLevel - 1)) * param1 + param2);
            }
        default:
            break;
    }
    return 0;
}

int NFQuestUtil::OnQuestComplete(NFPlayer* pPlayer, int iQuestId)
{
    CHECK_NULL(0, pPlayer);
    NFQuestPart* pQuestPart = pPlayer->GetPart<NFQuestPart>();
    CHECK_NULL(0, pQuestPart);

    proto_ff::Proto_GCFinishQuestNtf finishQuestNtf;
    finishQuestNtf.set_role_id(pPlayer->GetRoleID());
    finishQuestNtf.set_finish_quest_id(iQuestId);

    pQuestPart->MakeCityPlayerData(finishQuestNtf.mutable_quest_data());

    pPlayer->SendMsgToCityServer(E_GC_CITYSVR_FINISH_QUEST_NTF, finishQuestNtf);
    return 0;
}
